/*
 * invulgotracker
 * Copyright (C) Thomas Andersen 2011 <thomas.1.andersen@gmail.com>
 *
 * invulgotracker is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * invulgotracker is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "time-used-thread.h"
#include "config.h"
#include "gettextmacro.h"

TimeUsedThread::TimeUsedThread()
{
    killed = false;
    done = false;
    prevTime = NULL;
    isThread = false;
    pthread_mutex_init(&statusMutex, 0);
}

int TimeUsedThread::Start()
{
    int rc = 0;

    /* Start thread. */
    isThread = true;
    pthread_attr_t attr;
    rc = pthread_attr_init(&attr);
    if (!rc) {
        rc = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
        if (!rc) {
            rc = pthread_create(&thread, &attr, TimeUsedThread::Run, (void *)this);
        }
        pthread_attr_destroy(&attr);
    }

    return rc;
}

void TimeUsedThread::Calc()
{
    Day dayfinder(-1, -1, -1);
    dayfinder.FindFiles();
    int y,m,d;
    while(!killed && dayfinder.FindNext(y,m,d)) {
        Day dayloader(y,m,d);
        dayloader.Load(this);

        if (prevTime) {
            delete prevTime;
            prevTime = NULL;
        }
    }
}


void TimeUsedThread::Data(gchar *name, gchar *timeStr, TaskId id)
{
    if (killed)
        return;
    bool found = false;
    TaskTime taskTime(timeStr);
    list <task>::iterator iter;
    for(iter = taskList.begin(); iter != taskList.end(); ++iter) {
        if ((*iter).id == id) {
            found = true;
            break;
        }
    }
    if (prevTime) {
        TaskTime diff = taskTime.SubTime(*prevTime);
        list <task>::iterator itemiter;
        for(itemiter = taskList.begin(); itemiter != taskList.end(); ++itemiter) {
            if ((*itemiter).id == prevId) {
                (*itemiter).usedTime = (*itemiter).usedTime.AddTime(diff);
                break;
            }
        }
        delete prevTime;
    }
    prevTime = new TaskTime(taskTime);
    prevId = id;
    if (!found) {
        task t;
        t.id = id;
        t.usedTime = TaskTime("00:00");
        taskList.push_back(t);
    }

    /* Free */
    g_free(name);
    g_free(timeStr);

}

void TimeUsedThread::UpdateParents(TaskView &taskView)
{
    taskListTmp.clear();
    list <task>::const_iterator itemiter;
    for(itemiter = taskList.begin(); itemiter != taskList.end(); ++itemiter) {
        bool found = false;
        list <task>::const_iterator iter;
        for(iter = taskListTmp.begin(); iter != taskListTmp.end(); ++iter) {
            if ((*iter).id == (*itemiter).id) {
                found = true;
                break;
            }
        }
        if (!found)
            taskListTmp.push_back(*itemiter);
        AddToParent(taskView, (*itemiter).id, (*itemiter).usedTime);
    }
    taskList = taskListTmp;
    taskListTmp.clear();
}


void TimeUsedThread::AddToParent(TaskView &taskView, const TaskId &id, const TaskTime &diff)
{
    gchar *name = NULL;
    TaskId parent = id;

    if (taskView.GetParent(parent, &name)) {
        /* See if exist. */
        bool found = false;
        list <task>::iterator iter;
        for(iter = taskListTmp.begin(); iter != taskListTmp.end(); ++iter) {
            if ((*iter).id == parent) {
                found = true;
                break;
            }
        }
        /* Create if not. */
        if (!found) {
            task t;
            t.id = parent;
            t.usedTime = TaskTime("00:00");
            taskListTmp.push_back(t);
        }
        /* Add to task. */
        for(iter = taskListTmp.begin(); iter != taskListTmp.end(); ++iter) {
            if ((*iter).id == parent) {
                (*iter).usedTime = (*iter).usedTime.AddTime(diff);
                break;
            }
        }
        AddToParent(taskView, parent, diff);
    }
    g_free(name);
}



void *TimeUsedThread::Run(void *data)
{
    TimeUsedThread *t = (TimeUsedThread *)data;

    t->Calc();

    pthread_mutex_lock(&t->statusMutex);
    t->done = true;
    pthread_mutex_unlock(&t->statusMutex);

    pthread_exit(NULL);
}

bool TimeUsedThread::IsDone()
{
    bool isDone;
    pthread_mutex_lock(&statusMutex);
    isDone = done;
    pthread_mutex_unlock(&statusMutex);
    return isDone;
}

TimeUsedThread::~TimeUsedThread()
{
    /* Cancel or end thread */
    killed = true;
    if (isThread)
        pthread_join(thread, NULL);

    /* Cleanup. */
    pthread_mutex_destroy(&statusMutex);
    if (prevTime) {
        delete prevTime;
        prevTime = NULL;
    }
}

void TimeUsedThread::UpdateView(TimeView &timeView, TaskView &taskView)
{
    list <task>::iterator iter;
    Prefs prefs;

    for(iter = taskList.begin(); iter != taskList.end(); ++iter) {
        glong estimate = taskView.GetEstimate95((*iter).id);
        estimate *= 60;
        glong used = (*iter).usedTime.Minutes();
        glong pct;
        if (estimate)
            pct = (glong)(((gdouble)used/(gdouble)estimate)*100.0);
        else
            pct = 0;
        if (pct > 100)
            pct = 100;
        timeView.SetPct((*iter).id, pct);
        if (taskView.GetDone((*iter).id))
            timeView.SetColor((*iter).id, prefs.GetColorDone());
        else if (taskView.GetHasDeadline((*iter).id)) {
            glong deadline = taskView.GetDeadline((*iter).id);
            TaskTime tt;
            glong now = (tt.GetYear()*10000)+(tt.GetMonth()*100)+tt.GetDay();
            if (now >= deadline)
                timeView.SetColor((*iter).id, prefs.GetColorDeadline());
        } else {
            timeView.SetColor((*iter).id, (gchar *)NULL);
        }
    }
}


bool TimeUsedThread::Begin()
{
    iter = taskList.begin();
    return iter != taskList.end();
}

bool TimeUsedThread::Next()
{
    ++iter;
    return iter != taskList.end();
}

const TaskId &TimeUsedThread::GetTaskId()
{
    return (*iter).id;
}

const TaskTime &TimeUsedThread::GetTaskTime()
{
    return (*iter).usedTime;
}




